package fetcher

import (
	"crypto/tls"
	"errors"
	"io/ioutil"
	"learn/smart/config"
	"net/http"
	"net/url"
	"strings"
	"time"
	"learn/smart/util/redis"
	"learn/smart/util"
)

type Fetcher struct {
	BaseUrl string
	headers map[string]string
	proxy   string
}

var logger util.Logger

func init() {
	logger = util.Logger{Name: "Fetcher"}
}

var proxies = config.GetProxies()

var rateLimiter = time.Tick(250 * time.Millisecond)

func (f *Fetcher) getFullUrl(path string) string {
	return f.BaseUrl + path
}

func (f *Fetcher) GetClient() *http.Client {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	proxy, err := f.getProxy()

	if err == nil {
		tr.Proxy = http.ProxyURL(proxy)
	}

	client := &http.Client{
		Transport: tr,
		Timeout:   time.Second * 30, //超时时间
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse //禁止跳转
		},
	}

	return client
}

func (f *Fetcher) getProxy() (*url.URL, error) {
	if config.OpenProxy() == false {
		return nil, errors.New("不使用代理服务器访问")
	}
	var proxyUrl string

	if len(proxies) == 0 {
		proxies = config.GetProxies()
	}

	proxyUrl = proxies[0]
	proxies = proxies[1:]
	f.proxy = proxyUrl

	logger.Message("代理: %s", proxyUrl)

	return url.Parse(proxyUrl)
}

func (f *Fetcher) Get(path string, headers map[string]string) (*http.Response, error) {
	r, err := http.NewRequest(http.MethodGet, f.getFullUrl(path), nil)
	if err != nil {
		return nil, err
	}
	f.buildHeaders(r, headers)

	return f.clientDo(r)
}

func (f *Fetcher) clientDo(r *http.Request) (*http.Response, error) {
	<-rateLimiter
	var resp *http.Response
	var err error
	for i := 0; i < 3; i++ {
		client := f.GetClient()
		resp, err = client.Do(r)

		if err == nil {
			f.saveSuccessProxy()
			break
		}
		logger.Message("请求失败, 重试")
		f.saveFailedProxy()
	}

	return resp, err
}

func (f *Fetcher) saveFailedProxy() {
	rClient := redis.GetClient()
	rClient.HIncrBy("failed_proxies", f.proxy, 1)
}

func (f *Fetcher) saveSuccessProxy() {
	rClient := redis.GetClient()
	rClient.HIncrBy("success_proxies", f.proxy, 1)
}

func GetContents(resp *http.Response) ([]byte, error) {
	return ioutil.ReadAll(resp.Body)
}

func (f *Fetcher) buildHeaders(r *http.Request, headers map[string]string) {
	r.Header.Set("User-Agent",
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36")
	if headers != nil {
		for name, value := range headers {
			r.Header.Set(name, value)
		}
	}

	if f.headers != nil {
		for name, value := range f.headers {
			r.Header.Set(name, value)
		}
	}
}

func (f *Fetcher) Post(path string, data url.Values, headers map[string]string) (*http.Response, error) {
	r, err := http.NewRequest(http.MethodPost, f.getFullUrl(path), strings.NewReader(data.Encode()))
	if err != nil {
		return nil, err
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	f.buildHeaders(r, headers)

	return f.clientDo(r)
}

func (f *Fetcher) SetHeader(name string, value string) {
	if f.headers == nil {
		f.headers = make(map[string]string)
	}

	f.headers[name] = value
}

func (f *Fetcher) GetHeaders() map[string]string {
	return f.headers
}
